package com.kingmed.kmss.common.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.kingmed.kmss.common.annotation.FieldCompare;
import com.kingmed.kmss.common.constant.common.OperatorConstants;
import com.kingmed.kmss.common.dto.base.ChangeLogDTO;
import lombok.extern.slf4j.Slf4j;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 业务内容变更字段比较 实现工具类
 *
 * @author wyl
 */
@Slf4j
public class FieldCompareUtil {

    /**
     * 字段变更比较实现
     * <p>
     * FieldCompare(chineseName = "项目类型",properties="1:是,0:否")
     */
    public static <T> List<ChangeLogDTO> compare(Class<T> type, T newObject, T oldObject) {
        List<ChangeLogDTO> logList = new ArrayList<>();

        Class<?> newObj = newObject.getClass();
        Field[] newFields = type.getDeclaredFields();
        for (int i = 0; i < newFields.length; i++) {
            FieldCompare newAnnotation = newFields[i].getAnnotation(FieldCompare.class);
            if (null != newAnnotation) {
                try {
                    PropertyDescriptor newPd = new PropertyDescriptor(newFields[i].getName(), newObj);
                    Method getMethodNew = newPd.getReadMethod();
                    Object oldVal = getMethodNew.invoke(oldObject);
                    Object newVal = getMethodNew.invoke(newObject);
                    boolean eq = false;
                    if((oldVal == null || StrUtil.isBlank(oldVal.toString()))
                            && (newVal == null || StrUtil.isBlank(newVal.toString()))){
                        eq = false;
                    } else if(oldVal == null && newVal!= null){
                        eq = true;
                    } else if (oldVal instanceof String) {
                        String s1 = checkString(oldVal);
                        String s2 = checkString(newVal);
                        eq = !s1.equals(s2);
                    } else if (oldVal instanceof Integer) {
                        int i1 = (Integer) oldVal;
                        int i2 = (Integer) newVal;
                        eq = i1 != i2;
                    } else if (oldVal instanceof Double) {
                        double d1 = (Double) oldVal;
                        double d2 = (Double) newVal;
                        eq = d1 != d2;
                    } else if (oldVal instanceof BigDecimal) {
                        BigDecimal b1 = (BigDecimal) oldVal;
                        BigDecimal b2 = (BigDecimal) newVal;
                        eq = b1.compareTo(b2) != 0;
                    } else if (oldVal instanceof Long) {
                        long l1 = (Long) oldVal;
                        long l2 = (Long) newVal;
                        eq = l1 != l2;
                    } else if (oldVal instanceof Date) {
                        String l1 = String.valueOf(oldVal);
                        String l2 = String.valueOf(newVal);
                        eq = !l1.equals(l2);
                    }
                    String s1 = checkString(oldVal);
                    String s2 = checkString(newVal);
                    if (eq) {
                        Map<String, String> codeNameMap = codeToNameMap(newAnnotation);
                        ChangeLogDTO changeLog = new ChangeLogDTO();
                        changeLog.setLogType(ChangeLogDTO.LOG_TYPE1);
                        changeLog.setChangeTitle(newAnnotation.chineseName());
                        changeLog.setChangeBefore(codeNameMap.get(s1) == null ? s1 : codeNameMap.get(s1));
                        changeLog.setChangeAfter(codeNameMap.get(s2) == null ? s2 : codeNameMap.get(s2));
                        logList.add(changeLog);
                    } else if (ChangeLogDTO.LOG_TYPE2.equals(newAnnotation.type())) {
                        String oldValStr = JSONUtil.toJsonStr(oldVal);
                        String newValStr = JSONUtil.toJsonStr(newVal);
                        if ((StrUtil.isBlank(oldValStr) && StrUtil.isNotEmpty(newValStr))
                                || !StringUtils.equals(oldValStr, newValStr)) {
                            ChangeLogDTO changeLog = new ChangeLogDTO();
                            changeLog.setLogType(ChangeLogDTO.LOG_TYPE2);
                            changeLog.setChangeTitle(newAnnotation.chineseName());
                            changeLog.setJsonBefore(JSONUtil.toJsonStr(oldVal));
                            changeLog.setJsonAfter(JSONUtil.toJsonStr(newVal));
                            logList.add(changeLog);
                        }
                    }
                } catch (Exception e) {
                    log.error("业务字段变更比较异常：", e);
                }
            }
        }
        return logList;
    }

    private static Map<String, String> codeToNameMap(FieldCompare newAnnotation) {
        Map<String, String> map = new HashMap<>();
        String properties = newAnnotation.properties();
        if (StringUtils.isNotBlank(properties)) {
            String[] propertiesArr = properties.split(",");
            for (String prop : propertiesArr) {
                String[] split = prop.split(":");
                map.put(split[0], split[1]);
            }
        }
        return map;
    }

    private static String checkString(Object obj) {
        if (obj !=null && obj instanceof Date) {
            //日期转换有问题
//            return DateTimeUtil.dateStrToFormat(obj.toString());
            return DateUtil.format((Date) obj,"yyyy-MM-dd");
        }
        String str = (obj == null) ? "" : obj.toString().trim();
        if (str.equals("null")) {
            str = "";
        }
        if (str.contains("@_@")) {
            str = str.replaceAll("@_@", OperatorConstants.DH_KEY);
        }
        return str;
    }
}
